/*
 * Copyright (c) Huawei Device Co., Ltd. 2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "ref_do.h"
#include "common_utils.h"

namespace OHOS {
namespace SE {
using namespace std;
static constexpr int AID_HEAD_LENGTH = 2;

/*
 * Software that is part of the Secure Element access API, it obtains access
 * rules from the Secure Element and applies those rules to restrict device
 * application access to the various Secure Element applications.
 */
RefDo::RefDo(std::vector<uint8_t> rawData)
    : BerTlv(rawData, REF_DO_TAG)
{
    rawData_ = rawData;
    doTag_ = REF_DO_TAG;
}

RefDo::RefDo(std::vector<uint8_t> payload, bool isPayload)
    : BerTlv(payload, isPayload, REF_DO_TAG)
{
    doTag_ = REF_DO_TAG;
}

RefDo::~RefDo()
{
}

bool RefDo::GetAidRefDoAndDeviceAppDo(vector<AidRefDo> &retAidRefDo, vector<DeviceAppIdRefDo> &retDeviceAppDo)
{
    uint32_t tag;
    if (!GetDoTag(tag) || tag != REF_DO_TAG) {
        ErrorLog("No REF_AR_DO tag = %{public}d.", tag);
        return false;
    }
    vector<uint8_t> payload;
    if (!GetDoPayload(payload)) {
        return false;
    }
    // parse refardo from payload
    if (payload.empty() || payload.size() == 0) {
        ErrorLog("No REF_AR_DO.");
        return false;
    }
    uint32_t startIndex = 0;
    uint32_t length = 0;
    std::vector<uint8_t> aidRefDoData;
    std::vector<uint8_t> deviceAppDoData;
    do {
        // get aidRefDo
        if (payload[startIndex] != AID_REF_DO_TAG && payload[startIndex] != AID_REF_DO_EMPTY_TAG) {
            ErrorLog("invalid AidRefDo header.");
            return false;
        }
        uint32_t tmpAidRefDoTag = payload[startIndex];
        length = payload[startIndex + 1];
        if (payload.size() < startIndex + 1 + length) {
            ErrorLog("invalid AidRefDo length.");
            return false;
        }
        vector<uint8_t> tmpAidRefDoData;
        tmpAidRefDoData.assign(payload.begin() + startIndex, payload.begin() + startIndex + length + AID_HEAD_LENGTH);
        std::string strForAidRefDo = CommonUtils::BytesArrayToHexString(&tmpAidRefDoData[0], tmpAidRefDoData.size());
        InfoLog("AidRefDo %{public}s. startIndex=%{public}d, length=%{public}d",
            strForAidRefDo.c_str(), startIndex, length);
        AidRefDo tmpAidRefDo = AidRefDo(tmpAidRefDoData, tmpAidRefDoTag);
        retAidRefDo.push_back(tmpAidRefDo);
        startIndex = startIndex + length + AID_HEAD_LENGTH;
        // get deviceAppIdRefDo
        if (payload[startIndex] != DEVICE_APPID_REF_DO_TAG) {
            ErrorLog("invalid ArDo header.payload[startIndex]=%{public}d", payload[startIndex]);
            return false;
        }
        length = payload[startIndex + 1];
        if (payload.size() < startIndex + 1 + length) {
            ErrorLog("invalid ArDo length.");
            return false;
        }
        vector<uint8_t> tmpDeviceAppIdRefDoData;
        tmpDeviceAppIdRefDoData.assign(payload.begin() + startIndex, payload.begin() +
            startIndex + length + AID_HEAD_LENGTH);
        DeviceAppIdRefDo tmpDeviceAppIdRefDo = DeviceAppIdRefDo(tmpDeviceAppIdRefDoData);
        retDeviceAppDo.push_back(tmpDeviceAppIdRefDo);
        startIndex = startIndex + length + AID_HEAD_LENGTH;
    } while (0 /* startIndex + length <= payload.size() */);
    return true;
}

bool RefDo::GetAidRefDo(vector<AidRefDo> &retAidRefDo)
{
    uint32_t tag;
    if (!GetDoTag(tag) || tag != REF_DO_TAG) {
        return false;
    }
    vector<uint8_t> payload;
    if (!GetDoPayload(payload)) {
        return false;
    }
    // parse refardo from payload
    if (payload.empty() || payload.size() == 0) {
        ErrorLog("No REF_AR_DO.");
        return false;
    }
    uint32_t startIndex = 0;
    uint32_t length = 0;
    std::vector<uint8_t> aidRefDoData;
    std::vector<uint8_t> deviceAppDoData;
    do {
        // get aidRefDo
        if (payload[startIndex] != AID_REF_DO_TAG && payload[startIndex] != AID_REF_DO_EMPTY_TAG) {
            ErrorLog("invalid AidRefDo header.");
            return false;
        }
        uint32_t tmpAidRefDoTag = payload[startIndex];
        length = payload[startIndex + 1];
        if (payload.size() < startIndex + 1 + length) {
            ErrorLog("invalid AidRefDo length.");
            return false;
        }
        vector<uint8_t> tmpAidRefDoData;
        tmpAidRefDoData.assign(payload.begin() + startIndex, payload.begin() + startIndex + length + AID_HEAD_LENGTH);
        AidRefDo tmpAidRefDo = AidRefDo(tmpAidRefDoData, tmpAidRefDoTag);
        retAidRefDo.push_back(tmpAidRefDo);
        startIndex = startIndex + length + AID_HEAD_LENGTH;
    } while (0 /* startIndex + length <= payload.size() */);
    return true;
}

bool RefDo::GetDeviceAppDo(vector<DeviceAppIdRefDo> &retDeviceAppDo)
{
    uint32_t tag;
    if (!GetDoTag(tag) || tag != REF_DO_TAG) {
        return false;
    }
    vector<uint8_t> payload;
    if (!GetDoPayload(payload)) {
        return false;
    }
    // parse refardo from payload
    if (payload.empty() || payload.size() == 0) {
        ErrorLog("No REF_AR_DO.");
        return false;
    }
    uint32_t startIndex = 0;
    uint32_t length = 0;
    std::vector<uint8_t> aidRefDoData;
    std::vector<uint8_t> deviceAppDoData;
    do {
        // get deviceAppIdRefDo
        if (payload[startIndex] != DEVICE_APPID_REF_DO_TAG) {
            if (payload.size() > startIndex + TAG_LENGTH_TWO_BYTES &&
                payload[startIndex + TAG_LENGTH_TWO_BYTES] != DEVICE_APPID_REF_DO_TAG) {
                ErrorLog("invalid ArDo header.");
                return false;
            } else {
                startIndex = TAG_LENGTH_TWO_BYTES; // TAG_LENGTH_TWO_BYTES means to delete 4F00 or 4C00
            }
        }
        if (payload.size() <= startIndex + 1) {
            ErrorLog("invalid ArDo length index.");
            return false;
        }
        length = payload[startIndex + 1];
        if (payload.size() < startIndex + 1 + length) {
            ErrorLog("invalid ArDo length.");
            return false;
        }
        vector<uint8_t> tmpDeviceAppIdRefDoData;
        tmpDeviceAppIdRefDoData.assign(payload.begin() + startIndex, payload.begin() +
            startIndex + length + AID_HEAD_LENGTH);
        DeviceAppIdRefDo tmpDeviceAppIdRefDo = DeviceAppIdRefDo(tmpDeviceAppIdRefDoData);
        retDeviceAppDo.push_back(tmpDeviceAppIdRefDo);
        startIndex = startIndex + length + AID_HEAD_LENGTH;
    } while (startIndex + length <= payload.size());
    return true;
}

}  // namespace SE
}  // namespace OHOS
